#ifndef __CMATRIX3D_H__
#define __CMATRIX3D_H__

#include "StdDefs.h"
#include "CMathFixed.h"
#include "CVector3d.h"
#include "CMatrix4d.h"
#include "CMatrix4dh.h"


// CMatrix3d* must maintain casting ability with (fixed*).
// CMatrix3d may not have virtuals, inherited, or instantiated data.
 

class CMatrix3d
{
public:
	static const fixed LookAtUpVectorTolerance;

public:
	CMatrix3d();
	CMatrix3d( const fixed* m );
	CMatrix3d( fixed m00, fixed m01, fixed m02, 
			   fixed m10, fixed m11, fixed m12,
			   fixed m20, fixed m21, fixed m22,
			   int16 nOfArthmeticOpsTriggeringOrientationFixup = 0 ); 
			// parameter columns are rows in the matrix!
	CMatrix3d( const CVector3d* col,
			   int16 nOfArthmeticOpsTriggeringOrientationFixup = 0 );
	CMatrix3d( const CVector3d& col0, const CVector3d& col1, const CVector3d& col2,
			   int16 nOfArthmeticOpsTriggeringOrientationFixup = 0 );
	CMatrix3d( const CMatrix3d& m );
	CMatrix3d( const CMatrix4dh& m );
	CMatrix3d( const CMatrix4d& m );
	~CMatrix3d(){}

	CMatrix3d&			Clear();
	CMatrix3d&			LoadIdentity();
	CMatrix3d&			Orthonormalize();
	CMatrix3d&			Scale( fixed si, fixed sj, fixed sk );
	CMatrix3d&			Scale( const CVector3d& s );
	CMatrix3d&			RotateX( fixed degrees );
	CMatrix3d&			RotateY( fixed degrees );
	CMatrix3d&			RotateZ( fixed degrees );
	CMatrix3d&			Rotate( fixed degrees, fixed i, fixed j, fixed k );
	CMatrix3d&			Rotate( fixed degrees, const CVector3d& axis );
	CMatrix3d&			Translate( fixed i, fixed j, fixed k );
	CMatrix3d&			Translate( const CVector3d& v );
	CMatrix3d&			LookAtRHUpJ( fixed i, fixed j, fixed k );
	CMatrix3d&			LookAtRHUpJ( const CVector3d& direction );
	CMatrix3d&			LookAtRHUpK( fixed i, fixed j, fixed k );
	CMatrix3d&			LookAtRHUpK( const CVector3d& direction );

	CMatrix3d&			Transpose();

	CMatrix3d&			operator=( const fixed* col ); 
	CMatrix3d&			operator=( const CVector3d* col );

	CMatrix3d&			operator*=( const CMatrix3d& b );
	//CMatrix3d&		operator+=( const 

	fixed&				operator[]( int idx );
	fixed const&		operator[]( int idx ) const;

	friend CMatrix3d	operator*( const CMatrix3d& a, const CMatrix3d& b );

	int16				GetNumOfArithmeticOpsSinceLastFixup() const;
	void				SetNumOfArithmeticOpsTriggeringOrientationFixup( int16 val );
	int16				GetNumOfArithmeticOpsTriggeringOrientationFixup() const;

	CVector3d			m_col0;
	CVector3d			m_col1;
	CVector3d			m_col2;

protected:
	void				IncrementOperationCount();
	void				ConsiderOrientationFixup();

	boolean				m_orientationFixupIsApplicable;
	uint8				m_reserved[3];
	uint16				m_nOfArithmeticOpsSinceLastFixup;
	uint16				m_nOfArthmeticOpsTriggeringOrientationFixup;
};




inline
CMatrix3d::CMatrix3d( const CMatrix3d& m )
{
	*this = m;
}


inline
CMatrix3d& CMatrix3d::Clear()
{
	MEMSET( this, 0, sizeof( CVector3d ) * 3 );

	m_nOfArithmeticOpsSinceLastFixup = 0;

	return *this;
}


inline
CMatrix3d& CMatrix3d::Scale( fixed si, fixed sj, fixed sk )
{
	m_col0 *= si;
	m_col1 *= sj;
	m_col2 *= sk;

	m_orientationFixupIsApplicable = FALSE;

	return *this;
}


inline
CMatrix3d& CMatrix3d::Scale( const CVector3d& s )
{
	m_col0 *= s.m_i;
	m_col1 *= s.m_j;
	m_col2 *= s.m_k;

	m_orientationFixupIsApplicable = FALSE;

	return *this;
}


inline
CMatrix3d& CMatrix3d::Rotate( fixed degrees, const CVector3d& axis )
{
	return Rotate( degrees, axis.m_i, axis.m_j, axis.m_k );
}


inline
CMatrix3d&	CMatrix3d::Translate( const CVector3d& v )
{
	return Translate( v.m_i, v.m_j, v.m_k );
}


inline
CMatrix3d& CMatrix3d::LookAtRHUpJ( const CVector3d& direction )
{
	return LookAtRHUpJ( direction.m_i, direction.m_j, direction.m_k );
}


inline
CMatrix3d& CMatrix3d::LookAtRHUpK( const CVector3d& direction )
{
	return LookAtRHUpK( direction.m_i, direction.m_j, direction.m_k );
}


inline
CMatrix3d& CMatrix3d::operator=( const fixed* m )
{ 
	ASSERT( m ); 

	MEMCPY( this, m, sizeof( CMatrix3d ) );

	ASSERT( !m_orientationFixupIsApplicable || 
			( m_orientationFixupIsApplicable && m_nOfArthmeticOpsTriggeringOrientationFixup ) );

	return *this;
}


inline
CMatrix3d& CMatrix3d::operator=( const CVector3d* col )
{ 
	ASSERT( col ); 

	MEMCPY( this, col, sizeof( CVector3d ) * 3 );

	m_nOfArithmeticOpsSinceLastFixup = 0;

	return *this;
}


inline
fixed& CMatrix3d::operator[]( int idx )
{
	ASSERT( idx < 9 );

	return *( ( (fixed*)this ) + idx );
}


inline
fixed const& CMatrix3d::operator[]( int idx ) const
{
	ASSERT( idx < 9 );

	return *( ( (fixed*)this ) + idx );
}


inline
int16 CMatrix3d::GetNumOfArithmeticOpsSinceLastFixup() const
{
	return m_nOfArithmeticOpsSinceLastFixup;
}


inline
void CMatrix3d::SetNumOfArithmeticOpsTriggeringOrientationFixup( int16 val )
{
	m_nOfArthmeticOpsTriggeringOrientationFixup = val;

	if( m_nOfArthmeticOpsTriggeringOrientationFixup )
		m_orientationFixupIsApplicable = TRUE;
	else
		m_orientationFixupIsApplicable = FALSE;
}


inline
int16 CMatrix3d::GetNumOfArithmeticOpsTriggeringOrientationFixup() const
{
	return m_nOfArthmeticOpsTriggeringOrientationFixup;
}




#endif // __CMATRIX3D_H__